package com.rjwh.dingdong.client.download;

import android.text.TextUtils;
import android.util.Log;

import com.rjwh.dingdong.client.application.MyApplication;
import com.rjwh.dingdong.client.bean.localbean.DownloadInfo;
import com.rjwh.dingdong.client.bean.localbean.LocalGmaeInfo;
import com.rjwh.dingdong.client.constant.LocalConstant;
import com.rjwh.dingdong.client.download.task.PriorityAsyncTask;
import com.rjwh.dingdong.client.util.FileUtils;
import com.rjwh.dingdong.client.util.LogUtil;
import com.rjwh.dingdong.client.util.RxBus;
import com.rjwh.dingdong.client.util.ZipUtils;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.text.NumberFormat;
import java.util.List;

import okhttp3.Request;
import okhttp3.Response;


public class DownloadTask extends PriorityAsyncTask<Void, DownloadInfo, DownloadInfo> {

    private static final int BUFFER_SIZE = 1024 * 8; //读写缓存大小
    private DownloadInfo mDownloadInfo;              //当前任务的信息
    private long mPreviousTime;                      //上次更新的时间，用于计算下载速度
    private boolean isRestartTask;                   //是否重新下载的标识位
    private boolean isPause;                         //当前任务是暂停还是停止， true 暂停， false 停止
    private static NumberFormat numberFormat = NumberFormat.getInstance();

    public DownloadTask(DownloadInfo downloadInfo, boolean isRestart) {
        mDownloadInfo = downloadInfo;
        isRestartTask = isRestart;
        //将当前任务在定义的线程池中执行
        executeOnExecutor(DownloadManager.getInstance().getThreadPool().getExecutor());
    }

    /**
     * 暂停的方法
     */
    public void pause() {
        if (mDownloadInfo.getDownloadState() == DownloadManager.STATE_WAITING) {
            //如果是等待状态的,因为该状态取消不会回调任何方法，需要手动触发
            mDownloadInfo.setDownloadState(DownloadManager.STATE_PAUSED);
            mDownloadInfo.setNetworkSpeed(0);
            LogUtil.d("暂停");
            postMessage(LocalConstant.RX__POST_DOWNLOAD_STATE_CHANGE);
        } else {
            isPause = true;
        }
        super.cancel(false);
    }

    /**
     * 停止的方法
     */
    public void stop() {
        if (mDownloadInfo.getDownloadState() == DownloadManager.STATE_PAUSED || //
                mDownloadInfo.getDownloadState() == DownloadManager.STATE_ERROR ||//
                mDownloadInfo.getDownloadState() == DownloadManager.STATE_WAITING) {
            //如果状态是暂停或错误的,停止不会回调任何方法，需要手动触发
            mDownloadInfo.setDownloadState(DownloadManager.STATE_NONE);
            mDownloadInfo.setNetworkSpeed(0);
            LogUtil.d("停止");
            postMessage(LocalConstant.RX__POST_DOWNLOAD_STATE_CHANGE);
        } else {
            isPause = false;
        }
        super.cancel(false);
    }

    /**
     * 每个任务进队列的时候，都会执行该方法
     */
    @Override
    protected void onPreExecute() {
        LogUtil.d("onPreExecute:" + mDownloadInfo.getGameName());

        //如果是重新下载，需要删除临时文件
        if (isRestartTask) {
            deleteFile(mDownloadInfo.getTargetPath());
            mDownloadInfo.setProgress(0);
            mDownloadInfo.setDownloadLength(0);
            mDownloadInfo.setTotalLength(0);
            isRestartTask = false;
        }
        mDownloadInfo.setDownloadState(DownloadManager.STATE_WAITING);
        mDownloadInfo.setNetworkSpeed(0);
        LogUtil.d("等待中....");
        postMessage(LocalConstant.RX__POST_DOWNLOAD_STATE_CHANGE);
    }

    /**
     * 如果调用了Cancel，就不会执行该方法，所以任务结束的回调不放在这里面
     */
    @Override
    protected void onPostExecute(DownloadInfo downloadInfo) {
    }

    /**
     * 一旦该方法执行，意味着开始下载了
     */
    @Override
    protected DownloadInfo doInBackground(Void... params) {
        if (isCancelled()) return mDownloadInfo;
        mPreviousTime = System.currentTimeMillis();
        mDownloadInfo.setNetworkSpeed(0);
        mDownloadInfo.setDownloadState(DownloadManager.STATE_DOWNLOADING);
        postMessage(LocalConstant.RX__POST_DOWNLOAD_STATE_CHANGE);
        //构建下载文件路径，如果有设置，就用设置的，否者就自己创建
        String url = mDownloadInfo.getUrl();
        String fileName = mDownloadInfo.getGameName() + mDownloadInfo.getVersion() + ".zip";
        if (TextUtils.isEmpty(fileName)) {
            fileName = getUrlFileName(url);
            mDownloadInfo.setFileName(fileName);
        }

        if (TextUtils.isEmpty(mDownloadInfo.getTargetPath())) {
            File file = new File(mDownloadInfo.getTargetFolder(), fileName);
            mDownloadInfo.setTargetPath(file.getAbsolutePath());
        }

        //检查手机上文件的有效性
        File file = new File(mDownloadInfo.getTargetPath());
        LogUtil.i("file:" + file.getPath() + " file.length()" + file.length() + "getDownloadLength()" + mDownloadInfo.getDownloadLength());
        long startPos = mDownloadInfo.getDownloadLength();
        LogUtil.i("本地下载数据库存储的信息" + startPos);

        //再次检查文件有效性，文件大小大于总文件大小
        if (startPos > mDownloadInfo.getTotalLength()) {
            mDownloadInfo.setNetworkSpeed(0);
            mDownloadInfo.setDownloadState(DownloadManager.STATE_ERROR);
            LogUtil.e("文件大小错误 ");
            postMessage(LocalConstant.RX__POST_DOWNLOAD_STATE_CHANGE);
            return mDownloadInfo;
        }
        if (startPos == mDownloadInfo.getTotalLength() && startPos > 0) {
            mDownloadInfo.setProgress(1);
            mDownloadInfo.setNetworkSpeed(0);
            mDownloadInfo.setDownloadState(DownloadManager.STATE_DOWNLOADED);
            LogUtil.e("文件长度错误 ");
            postMessage(LocalConstant.RX__POST_DOWNLOAD_STATE_CHANGE);
            return mDownloadInfo;
        }
        //设置断点写文件
        ProgressRandomAccessFile randomAccessFile;
        try {
            randomAccessFile = new ProgressRandomAccessFile(file, "rw", startPos);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            mDownloadInfo.setNetworkSpeed(0);
            mDownloadInfo.setDownloadState(DownloadManager.STATE_ERROR);
            LogUtil.e("写入文件错误 ");
            postMessage(LocalConstant.RX__POST_DOWNLOAD_STATE_CHANGE);
            return mDownloadInfo;
        }

        //构建请求体,默认使用get请求下载,设置断点头
        Response response;
        try {
            // 开始下载
            Request request = new Request.Builder().url(mDownloadInfo.getUrl()).header("RANGE",
                    "bytes=" + startPos + "-") // Http value set breakpoints RANGE
                    .build();
            response = com.rjwh.dingdong.client.network.OkHttpUtils.getInstance().newCall(request).execute();

        } catch (IOException e) {
            e.printStackTrace();
            mDownloadInfo.setNetworkSpeed(0);
            mDownloadInfo.setDownloadState(DownloadManager.STATE_ERROR);
            LogUtil.d("下载错误 ");
            postMessage(LocalConstant.RX__POST_DOWNLOAD_STATE_CHANGE);
            return mDownloadInfo;
        }
        //获取流对象，准备进行读写文件
        long totalLength = response.body().contentLength();
        if (mDownloadInfo.getTotalLength() == 0) {
            mDownloadInfo.setTotalLength(totalLength);
        }
        InputStream is = response.body().byteStream();
        //读写文件流
        try {
            download(is, randomAccessFile);
        } catch (IOException e) {
            e.printStackTrace();
            mDownloadInfo.setNetworkSpeed(0);
            mDownloadInfo.setDownloadState(DownloadManager.STATE_ERROR);
            LogUtil.d("读取流错误 ");
            postMessage(LocalConstant.RX__POST_DOWNLOAD_STATE_CHANGE);
            return mDownloadInfo;
        }

        //循环结束走到这里，a.下载完成     b.暂停      c.判断是否下载出错
        if (isCancelled()) {
            mDownloadInfo.setNetworkSpeed(0);
            if (isPause) mDownloadInfo.setDownloadState(DownloadManager.STATE_PAUSED); //暂停
            else mDownloadInfo.setDownloadState(DownloadManager.STATE_NONE);          //停止
            LogUtil.d("停止");
            postMessage(LocalConstant.RX__POST_DOWNLOAD_STATE_CHANGE);
        } else if (file.length() == mDownloadInfo.getTotalLength() && mDownloadInfo.getDownloadState() == DownloadManager.STATE_DOWNLOADING) {
            mDownloadInfo.setNetworkSpeed(0);
            mDownloadInfo.setDownloadState(DownloadManager.STATE_DOWNLOADED); //下载完成
            LogUtil.i("下载完成");
            postMessage(LocalConstant.RX__POST_DOWNLOAD_STATE_CHANGE);

            File descFile = null;
            if (mDownloadInfo.getType() == 1) {
                descFile = new File(FileUtils.getDrwabookDirDirectory() + mDownloadInfo.getGameId() + File.separator);
            } else if (mDownloadInfo.getType() == 2) {
                descFile = new File(FileUtils.getGameDirectory() + mDownloadInfo.getGameId() + File.separator);
            } else {
                descFile = new File(FileUtils.getGameDirectory() + mDownloadInfo.getGameId() + File.separator);
            }
            unzip(new File(mDownloadInfo.getTargetPath()), descFile);

        } else if (file.length() != mDownloadInfo.getDownloadLength()) {
            mDownloadInfo.setNetworkSpeed(0);
            mDownloadInfo.setDownloadState(DownloadManager.STATE_ERROR); //由于不明原因，文件保存有误
            LogUtil.d("文件保存错误");
            postMessage(LocalConstant.RX__POST_DOWNLOAD_STATE_CHANGE);
        }
        return mDownloadInfo;
    }

    private void postMessage(String tag) {
        MyApplication.db.update(mDownloadInfo,"gameId="+mDownloadInfo.getGameId()+" and type="+mDownloadInfo.getType());
        RxBus.get().post(tag, mDownloadInfo);
    }

    public void unzip(File srcFile, File descFile) {
        try {
            mDownloadInfo.setNetworkSpeed(0);
            mDownloadInfo.setDownloadState(DownloadManager.STATE_UNZIP);
            RxBus.get().post(LocalConstant.RX__POST_DOWNLOAD_STATE_UNZIP, mDownloadInfo);
            long start = System.currentTimeMillis();
            FileUtils.deleteFile(descFile);
            LogUtil.i("HTML", "解压文件地址:" + srcFile.getPath() + " 解压路径:" + descFile.getPath());
            ZipUtils.UnZipFolder(srcFile.getPath(), descFile.getPath());
            if (descFile.exists()) {
                LogUtil.i("HTML", mDownloadInfo.getGameId() + " 解压成功 耗费时间：" + (System.currentTimeMillis() - start));
                mDownloadInfo.setDownloadState(DownloadManager.STATE_UNZIP_SUCESS);
                inserlLocal(descFile);
                DownloadManager.getInstance().removeTask(mDownloadInfo.getGameId());
            } else {
                LogUtil.i("HTML", "解压目录为空" + mDownloadInfo.getGameId() + " 解压失败");
                mDownloadInfo.setDownloadState(DownloadManager.STATE_ERROR);
                mDownloadInfo.setProgress(0);
                MyApplication.db.update(mDownloadInfo, "gameId=" + mDownloadInfo.getGameId()+" and type="+mDownloadInfo.getType());
            }
        } catch (Exception e) {
            e.printStackTrace();
            LogUtil.i("HTML", mDownloadInfo.getGameId() + " 解压失败");
            FileUtils.deleteFile(descFile);
            mDownloadInfo.setDownloadState(DownloadManager.STATE_ERROR);
            mDownloadInfo.setProgress(0);
            mDownloadInfo.setNetworkSpeed(0);
            MyApplication.db.update(mDownloadInfo, "gameId=" + mDownloadInfo.getGameId()+" and type="+mDownloadInfo.getType());
        } finally {
            //无论解压成功还是失败都删除目标原文件
            FileUtils.deleteFile(srcFile);
            RxBus.get().post(LocalConstant.RX__POST_DOWNLOAD_STATE_UNZIP, mDownloadInfo);
        }
    }

    public void inserlLocal(File descFile) {
        List<LocalGmaeInfo> dbGamelist = MyApplication.db
                .findAllByWhere(
                        LocalGmaeInfo.class,
                        "pid="
                                + mDownloadInfo
                                .getGameId());
        if (dbGamelist.isEmpty()) {
            LocalGmaeInfo gminfo = new LocalGmaeInfo();
            gminfo.setType(mDownloadInfo.getType());
            gminfo.setPid(mDownloadInfo.getGameId());
            gminfo.setPname(mDownloadInfo
                    .getGameName());
            gminfo.setDownloadUrl(mDownloadInfo.getUrl());
            gminfo.setIsUpdate(0);
            gminfo.setPath(descFile.getPath());
            gminfo.setPic(mDownloadInfo.getGamePic());
            gminfo.setVersion(mDownloadInfo.getVersion());
            gminfo.setTotalLength(mDownloadInfo.getTotalLength());
            MyApplication.db.save(gminfo);
            LogUtil.i("HTML", "保存本地game");
        } else {
            LocalGmaeInfo gminfo = dbGamelist.get(0);
            gminfo.setIsUpdate(0);
            gminfo.setVersion(mDownloadInfo.getVersion());
            gminfo.setPname(mDownloadInfo.getGameName());
            gminfo.setPath(descFile.getPath());
            gminfo.setDownloadUrl(mDownloadInfo.getUrl());
            gminfo.setType(mDownloadInfo.getType());
            gminfo.setVersion(mDownloadInfo.getVersion());
            gminfo.setTotalLength(mDownloadInfo.getTotalLength());
            gminfo.setPic(mDownloadInfo.getGamePic());
            MyApplication.db.update(gminfo,"pid="+mDownloadInfo.getGameId()+" and type="+mDownloadInfo.getType());
            LogUtil.i("HTML", "更新本地game");
        }
    }

    /**
     * 通过 ‘？’ 和 ‘/’ 判断文件名
     */
    private String getUrlFileName(String url) {
        int index = url.lastIndexOf('?');
        String filename;
        if (index > 1) {
            filename = url.substring(url.lastIndexOf('/') + 1, index);
        } else {
            filename = url.substring(url.lastIndexOf('/') + 1);
        }
        return filename;
    }

    /**
     * 根据路径删除文件
     */
    private boolean deleteFile(String path) {
        if (TextUtils.isEmpty(path)) return true;
        File file = new File(path);
        if (!file.exists()) return true;
        if (file.isFile()) {
            boolean delete = file.delete();
//            L.e("deleteFile:" + delete + " path:" + path);
            return delete;
        }
        return false;
    }

    /**
     * 执行文件下载
     */
    private int download(InputStream input, RandomAccessFile out) throws IOException {
        if (input == null || out == null) return -1;

        byte[] buffer = new byte[BUFFER_SIZE];
        BufferedInputStream in = new BufferedInputStream(input, BUFFER_SIZE);
        int downloadSize = 0;
        int len;
        try {
            out.seek(out.length());
            while ((len = in.read(buffer, 0, BUFFER_SIZE)) != -1 && !isCancelled()) {
                out.write(buffer, 0, len);
                downloadSize += len;
            }
        } finally {
            try {
                out.close();
                in.close();
                input.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return downloadSize;
    }

    /**
     * 文件读写
     */
    private final class ProgressRandomAccessFile extends RandomAccessFile {
        private long lastDownloadLength = 0; //总共已下载的大小
        private long curDownloadLength = 0;  //当前已下载的大小（可能分几次下载）
        private long lastRefreshUiTime;

        public ProgressRandomAccessFile(File file, String mode, long lastDownloadLength) throws FileNotFoundException {
            super(file, mode);
            this.lastDownloadLength = lastDownloadLength;
            this.lastRefreshUiTime = System.currentTimeMillis();
        }

        @Override
        public void write(byte[] buffer, int offset, int count) throws IOException {
            super.write(buffer, offset, count);

            //已下载大小
            long downloadLength = lastDownloadLength + count;
            curDownloadLength += count;
            lastDownloadLength = downloadLength;
            mDownloadInfo.setDownloadLength(downloadLength);

            //计算下载速度
            long totalTime = (System.currentTimeMillis() - mPreviousTime) / 1000;
            if (totalTime == 0) {
                totalTime += 1;
            }
            long networkSpeed = curDownloadLength / totalTime;

            mDownloadInfo.setNetworkSpeed(networkSpeed);

            int pb100 = (int) (((float) downloadLength / (float) mDownloadInfo.getTotalLength()) * 100);

            //下载进度
            float progress = downloadLength * 1.0f / mDownloadInfo.getTotalLength();

            mDownloadInfo.setProgress(pb100);
            mDownloadInfo.setNetworkSpeed(networkSpeed);
            long curTime = System.currentTimeMillis();
            //每200毫秒刷新一次数据
            if (curTime - lastRefreshUiTime >= 100 || progress == 1.0f) {
                postMessage(LocalConstant.RX__POST_DOWNLOAD_STATE_PROGRES);
                LogUtil.i(mDownloadInfo.getDownloadLength() + " " + mDownloadInfo.getTotalLength() + " " + mDownloadInfo.getProgress());
                lastRefreshUiTime = System.currentTimeMillis();
            }
        }
    }
}
